package per.ethan.springboot.basic.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import per.ethan.springboot.basic.domain.ErrorCodeVO;
import per.ethan.springboot.basic.entity.BaseEntity;
import per.ethan.springboot.basic.entity.Page;
import per.ethan.springboot.basic.exception.ServiceException;
import per.ethan.springboot.basic.mapper.IBaseMapper;
import per.ethan.springboot.basic.property.StateEnum;
import per.ethan.springboot.basic.service.BaseService;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Ethan
 * @date 2020/06/18 14:49
 */
@Slf4j
public abstract class BaseServiceImpl<M extends IBaseMapper<Entity>, Entity extends BaseEntity>
        extends ServiceImpl<M, Entity> implements BaseService<Entity> {

    protected static final String CREATE_TIME = "create_time";

    protected static final String STATE = "state";

    protected static final String ID = "id";

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long create(Entity entity) {
        try {
            super.save(entity);

            return entity.getId();
        } catch (Exception e) {
            log.error("BaseServiceImpl create is error: {}", JSONUtil.toJsonStr(entity), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public List<Long> batchSave(List<Entity> entityList) {
        try {
            super.saveBatch(entityList);

            return entityList.stream()
                    .map(Entity::getId)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("BaseServiceImpl batchSave is error: {}", JSONUtil.toJsonStr(entityList), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean removeById(Serializable id) {
        try {
            return super.removeById(id);
        } catch (Exception e) {
            log.error("BaseServiceImpl removeById is error, id: {}", id, e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        try {
            return super.removeByIds(idList);
        } catch (Exception e) {
            log.error("BaseServiceImpl removeByIds is error, idList: {}", JSONUtil.toJsonStr(idList), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean logicRemoveById(Long id) {
        try {
            Entity entity = this.getTarget().newInstance();
            entity.setState(StateEnum.DELETE.getCode())
                    .setId(id);

            return super.updateById(entity);
        } catch (Exception e) {
            log.error("BaseServiceImpl logicRemoveById is error, id: {}", id, e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean logicRemoveByIds(List<Long> idList) {
        try {
            UpdateWrapper<Entity> updateWrapper = new UpdateWrapper<>();
            updateWrapper.in(ID, idList);

            Entity entity = this.getTarget().newInstance();
            entity.setState(StateEnum.DELETE.getCode());

            return super.update(entity, updateWrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl logicRemoveByIds is error, idList: {}", JSONUtil.toJsonStr(idList), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean removeByWhere(Entity entity) {
        try {
            LambdaQueryWrapper<Entity> wrapper = Wrappers.lambdaQuery(entity)
                    .eq(Entity::getState, StateEnum.NORMAL.getCode());

            return super.remove(wrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl removeByWhere is error: {}", JSONUtil.toJsonStr(entity), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean logicRemoveByWhere(Entity entity) {
        try {
            LambdaUpdateWrapper<Entity> wrapper = Wrappers.lambdaUpdate(entity)
                    .eq(Entity::getState, StateEnum.NORMAL.getCode())
                    .set(Entity::getState, StateEnum.DELETE.getCode());

            return super.update(wrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl logicRemoveByWhere is error: {}", JSONUtil.toJsonStr(entity), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean updateById(Entity entity) {
        try {
            if (null == entity.getId()) {
                throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, "ID不允许为空!"));
            }

            return super.updateById(entity);
        } catch (Exception e) {
            log.error("BaseServiceImpl updateById is error: {}", JSONUtil.toJsonStr(entity), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean updateBatchById(Collection<Entity> entityList) {
        try {
            return super.updateBatchById(entityList);
        } catch (Exception e) {
            log.error("BaseServiceImpl updateBatchById is error: {}", JSONUtil.toJsonStr(entityList), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<Entity> list() {
        try {
            QueryWrapper<Entity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq(STATE, StateEnum.NORMAL.getCode())
                    .orderByDesc(CREATE_TIME);

            return super.list(queryWrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl list is error", e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Entity getById(Serializable id) {
        try {
            QueryWrapper<Entity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq(STATE, StateEnum.NORMAL.getCode())
                    .eq(ID, id);

            return super.getOne(queryWrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl getById is error", e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<Entity> listByWhere(Entity entity) {
        try {
            LambdaQueryWrapper<Entity> wrapper = Wrappers.lambdaQuery(entity)
                    .eq(Entity::getState, StateEnum.NORMAL.getCode())
                    .orderByDesc(Entity::getCreateTime);

            return super.list(wrapper);
        } catch (Exception e) {
            log.error("BaseServiceImpl listByWhere is error: {}", JSONUtil.toJsonStr(entity), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Page<Entity> listByPage(Page<Entity> page) {
        if (page.getStartRowNum() >= page.getEndRowNum()) {
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, "分页查询时开始行必须小于结束行"));
        }
        //单页查询不允许超过500条，防止恶意调用page size过大导致内存溢出
        if (page.getPageSize() > Page.MAX_PAGE_SIZE) {
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, "超过允许查询的单页记录最大值"));
        }
        try {
            Map<String, Object> queryParams = new HashMap<>(4);
            Map<String, Object> conditions = page.getConditions();

            if (CollUtil.isNotEmpty(conditions)) {
                queryParams.putAll(conditions);
            }
            queryParams.put("startRowNum", page.getStartRowNum());
            queryParams.put("endRowNum", page.getEndRowNum());
            queryParams.put("pageSize", page.getPageSize());
            queryParams.put("orderFields", page.getOrderFields());

            List<Entity> rows = this.getBaseMapper().findByPage(queryParams);
            page.setRows(rows);
            Integer count = this.getBaseMapper().getCount(queryParams);
            page.setTotal(count);

            return page;
        } catch (Exception e) {
            log.error("BaseServiceImpl listByPage is error: {}", JSONUtil.toJsonStr(page), e);
            throw new ServiceException(new ErrorCodeVO(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()));
        }
    }

}
